import { prisma } from "@calcom/prisma/__mocks__/prisma";
import { describe, expect, it, vi, beforeEach } from "vitest";
import { TRPCError } from "@trpc/server";
import getIncompleteBookingSettingsHandler from "./getIncompleteBookingSettings.handler";

vi.mock("@calcom/prisma", () => ({
  prisma,
}));

vi.mock("@calcom/app-store/routing-forms/lib/enabledIncompleteBookingApps", () => ({
  enabledIncompleteBookingApps: ["salesforce"],
}));

describe("getIncompleteBookingSettings.handler", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe("Authorization - Personal Forms", () => {
    it("should throw NOT_FOUND when user tries to access another user's personal form", async () => {
      const _userA = { id: 1 };
      const userB = { id: 2 };
      const formId = "form-123";

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue(null);

      await expect(
        getIncompleteBookingSettingsHandler({
          ctx: { prisma, user: userB },
          input: { formId },
        })
      ).rejects.toThrow(TRPCError);

      await expect(
        getIncompleteBookingSettingsHandler({
          ctx: { prisma, user: userB },
          input: { formId },
        })
      ).rejects.toThrow("Form not found");
    });

    it("should allow user to access their own personal form", async () => {
      const user = { id: 1 };
      const formId = "form-123";
      const mockCredential = {
        id: 1,
        type: "salesforce_other_calendar",
        userId: user.id,
        teamId: null,
        appId: "salesforce",
        invalid: false,
        delegationCredentialId: null,
      };

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: user.id,
        teamId: null,
      });
      prisma.credential.findFirst.mockResolvedValue(mockCredential);

      const result = await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user },
        input: { formId },
      });

      expect(result).toBeDefined();
      expect(result.credentials).toHaveLength(1);
      expect(result.credentials[0]).toMatchObject({
        id: 1,
        type: "salesforce_other_calendar",
        userId: user.id,
        teamId: null,
        appId: "salesforce",
      });
    });
  });

  describe("Authorization - Team Forms", () => {
    it("should throw NOT_FOUND when non-team-member tries to access team form", async () => {
      const _teamMember = { id: 1 };
      const nonMember = { id: 2 };
      const _teamId = 100;
      const formId = "team-form-123";

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue(null);

      await expect(
        getIncompleteBookingSettingsHandler({
          ctx: { prisma, user: nonMember },
          input: { formId },
        })
      ).rejects.toThrow(TRPCError);
    });

    it("should allow team member to access team form credentials", async () => {
      const teamMember = { id: 1 };
      const teamId = 100;
      const formId = "team-form-123";
      const mockCredentials = [
        {
          id: 1,
          type: "salesforce_other_calendar",
          userId: null,
          teamId: teamId,
          appId: "salesforce",
          invalid: false,
          delegationCredentialId: null,
          user: null,
          team: { name: "Test Team" },
        },
      ];

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: teamMember.id,
        teamId: teamId,
      });
      prisma.team.findUnique.mockResolvedValue({
        id: teamId,
        parentId: null,
      });
      prisma.credential.findMany.mockResolvedValue(mockCredentials);

      const result = await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user: teamMember },
        input: { formId },
      });

      expect(result).toBeDefined();
      expect(result.credentials).toHaveLength(1);
      expect(result.credentials[0]).toMatchObject({
        id: 1,
        type: "salesforce_other_calendar",
        teamId: teamId,
        appId: "salesforce",
      });
    });
  });

  describe("Credential Sanitization", () => {
    it("should never return the 'key' field in credentials for personal forms", async () => {
      const user = { id: 1 };
      const formId = "form-123";
      const mockCredentialWithKey = {
        id: 1,
        type: "salesforce_other_calendar",
        userId: user.id,
        teamId: null,
        appId: "salesforce",
        invalid: false,
        delegationCredentialId: null,
        key: {
          access_token: "secret_access_token",
          refresh_token: "secret_refresh_token",
          instance_url: "https://example.salesforce.com",
        },
      };

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: user.id,
        teamId: null,
      });
      prisma.credential.findFirst.mockResolvedValue(mockCredentialWithKey);

      const result = await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user },
        input: { formId },
      });

      expect(result.credentials).toHaveLength(1);
      expect(result.credentials[0]).not.toHaveProperty("key");
      expect(result.credentials[0].id).toBe(1);
    });

    it("should never return the 'key' field in credentials for team forms", async () => {
      const teamMember = { id: 1 };
      const teamId = 100;
      const formId = "team-form-123";
      const mockCredentialsWithKey = [
        {
          id: 1,
          type: "salesforce_other_calendar",
          userId: null,
          teamId: teamId,
          appId: "salesforce",
          invalid: false,
          delegationCredentialId: null,
          user: null,
          team: { name: "Test Team" },
          key: {
            access_token: "secret_team_access_token",
            refresh_token: "secret_team_refresh_token",
          },
        },
      ];

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: teamMember.id,
        teamId: teamId,
      });
      prisma.team.findUnique.mockResolvedValue({
        id: teamId,
        parentId: null,
      });
      prisma.credential.findMany.mockResolvedValue(mockCredentialsWithKey);

      const result = await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user: teamMember },
        input: { formId },
      });

      expect(result.credentials).toHaveLength(1);
      expect(result.credentials[0]).not.toHaveProperty("key");
      expect(result.credentials[0].id).toBe(1);
    });

    it("should use safeCredentialSelect fields for team credentials", async () => {
      const teamMember = { id: 1 };
      const teamId = 100;
      const formId = "team-form-123";

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: teamMember.id,
        teamId: teamId,
      });
      prisma.team.findUnique.mockResolvedValue({
        id: teamId,
        parentId: null,
      });
      prisma.credential.findMany.mockResolvedValue([]);

      await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user: teamMember },
        input: { formId },
      });

      expect(prisma.credential.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          select: expect.objectContaining({
            id: true,
            type: true,
            userId: true,
            teamId: true,
            appId: true,
            invalid: true,
            delegationCredentialId: true,
          }),
        })
      );

      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const callArgs = (prisma.credential.findMany as any).mock.calls[0][0];
      expect(callArgs.select).not.toHaveProperty("key");
    });
  });

  describe("Organization Hierarchy", () => {
    it("should include parent org credentials when team has a parent", async () => {
      const teamMember = { id: 1 };
      const teamId = 100;
      const parentOrgId = 200;
      const formId = "team-form-123";
      const mockCredentials = [
        {
          id: 1,
          type: "salesforce_other_calendar",
          userId: null,
          teamId: teamId,
          appId: "salesforce",
          invalid: false,
          delegationCredentialId: null,
          user: null,
          team: { name: "Test Team" },
        },
        {
          id: 2,
          type: "salesforce_other_calendar",
          userId: null,
          teamId: parentOrgId,
          appId: "salesforce",
          invalid: false,
          delegationCredentialId: null,
          user: null,
          team: { name: "Parent Org" },
        },
      ];

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: teamMember.id,
        teamId: teamId,
      });
      prisma.team.findUnique.mockResolvedValue({
        id: teamId,
        parentId: parentOrgId,
      });
      prisma.credential.findMany.mockResolvedValue(mockCredentials);

      const result = await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user: teamMember },
        input: { formId },
      });

      expect(prisma.credential.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({
            teamId: {
              in: [teamId, parentOrgId],
            },
          }),
        })
      );

      expect(result.credentials).toHaveLength(2);
      expect(result.credentials.map((c) => c.teamId)).toContain(teamId);
      expect(result.credentials.map((c) => c.teamId)).toContain(parentOrgId);
    });

    it("should only include team credentials when team has no parent", async () => {
      const teamMember = { id: 1 };
      const teamId = 100;
      const formId = "team-form-123";

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: teamMember.id,
        teamId: teamId,
      });
      prisma.team.findUnique.mockResolvedValue({
        id: teamId,
        parentId: null,
      });
      prisma.credential.findMany.mockResolvedValue([]);

      await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user: teamMember },
        input: { formId },
      });

      expect(prisma.credential.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({
            teamId: {
              in: [teamId],
            },
          }),
        })
      );
    });
  });

  describe("App Filtering", () => {
    it("should only return credentials for enabled incomplete booking apps", async () => {
      const user = { id: 1 };
      const formId = "form-123";

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: user.id,
        teamId: null,
      });
      prisma.credential.findFirst.mockResolvedValue(null);

      await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user },
        input: { formId },
      });

      expect(prisma.credential.findFirst).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({
            appId: {
              in: ["salesforce"],
            },
          }),
        })
      );
    });

    it("should filter team credentials by enabled apps", async () => {
      const teamMember = { id: 1 };
      const teamId = 100;
      const formId = "team-form-123";

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: teamMember.id,
        teamId: teamId,
      });
      prisma.team.findUnique.mockResolvedValue({
        id: teamId,
        parentId: null,
      });
      prisma.credential.findMany.mockResolvedValue([]);

      await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user: teamMember },
        input: { formId },
      });

      expect(prisma.credential.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({
            appId: {
              in: ["salesforce"],
            },
          }),
        })
      );
    });
  });

  describe("Edge Cases", () => {
    it("should return empty credentials array when no credentials exist for personal form", async () => {
      const user = { id: 1 };
      const formId = "form-123";

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: user.id,
        teamId: null,
      });
      prisma.credential.findFirst.mockResolvedValue(null);

      const result = await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user },
        input: { formId },
      });

      expect(result.credentials).toEqual([]);
      expect(result.incompleteBookingActions).toBeDefined();
    });

    it("should return empty credentials array when no credentials exist for team form", async () => {
      const teamMember = { id: 1 };
      const teamId = 100;
      const formId = "team-form-123";

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: teamMember.id,
        teamId: teamId,
      });
      prisma.team.findUnique.mockResolvedValue({
        id: teamId,
        parentId: null,
      });
      prisma.credential.findMany.mockResolvedValue([]);

      const result = await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user: teamMember },
        input: { formId },
      });

      expect(result.credentials).toEqual([]);
      expect(result.incompleteBookingActions).toBeDefined();
    });

    it("should throw NOT_FOUND when form does not exist", async () => {
      const user = { id: 1 };
      const formId = "non-existent-form";

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue([]);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue(null);

      await expect(
        getIncompleteBookingSettingsHandler({
          ctx: { prisma, user },
          input: { formId },
        })
      ).rejects.toThrow(TRPCError);

      await expect(
        getIncompleteBookingSettingsHandler({
          ctx: { prisma, user },
          input: { formId },
        })
      ).rejects.toThrow("Form not found");
    });

    it("should return incompleteBookingActions along with credentials", async () => {
      const user = { id: 1 };
      const formId = "form-123";
      const mockActions = [
        { id: 1, formId, action: "create_contact" },
        { id: 2, formId, action: "update_lead" },
      ];

      prisma.app_RoutingForms_IncompleteBookingActions.findMany.mockResolvedValue(mockActions);
      prisma.app_RoutingForms_Form.findFirst.mockResolvedValue({
        id: formId,
        userId: user.id,
        teamId: null,
      });
      prisma.credential.findFirst.mockResolvedValue(null);

      const result = await getIncompleteBookingSettingsHandler({
        ctx: { prisma, user },
        input: { formId },
      });

      expect(result.incompleteBookingActions).toEqual(mockActions);
      expect(result.incompleteBookingActions).toHaveLength(2);
    });
  });
});
